﻿using System;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
using System.Xml.Schema;
using NUnit.Framework;

namespace CodeContrib.Tests
{
	public class XmlSerializationExtensionsTests
	{
		private readonly Assembly _executingAssembly;

		public XmlSerializationExtensionsTests()
		{
			_executingAssembly = Assembly.GetExecutingAssembly();
		}

		[Test]
		public void DeserializeXml_With_Ascii_Encoding()
		{
			// Arrange
			Company expected = new MockCompany();

			var encoding = Encoding.ASCII;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void DeserializeXml_With_BigEndianUnicode_Encoding()
		{
			// Arrange
			Company expected = new MockCompany();

			var encoding = Encoding.BigEndianUnicode;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void DeserializeXml_With_Default_Encoding()
		{
			// Arrange
			Company expected = new MockCompany();

			var encoding = Encoding.Default;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void DeserializeXml_With_Unicde_Encoding()
		{
			// Arrange
			Company expected = new MockCompany();

			var encoding = Encoding.Unicode;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void DeserializeXml_With_Utf32_Encoding()
		{
			// Arrange
			var expected = new MockCompany();

			var encoding = Encoding.UTF32;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		[ExpectedException(typeof (InvalidOperationException))]
		public void DeserializeXml_With_Utf7_Encoding_ShouldThrow_InvalidOperationException()
		{
			// Arrange
			var company = new MockCompany();

			var encoding = Encoding.UTF7;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			companyXml.DeserializeXml<Company>(encoding);
		}

		[Test]
		public void DeserializeXml_With_Utf8_Encoding()
		{
			// Arrange
			Company expected = new MockCompany();

			var encoding = Encoding.UTF8;

			const string defaultNamespace = "http://www.test.com/ns";

			var companyXml = GetCompanyXml(expected, encoding, defaultNamespace);

			// Act
			var actual = companyXml.DeserializeXml<Company>(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		public static string GetCompanyXml(Company company, Encoding encoding, string defaultNamespace)
		{
			XNamespace ns = defaultNamespace;

			var xml = new XDocument(new XElement(ns + "company", new XElement("id", company.Id), new XElement("uri", company.Uri), new XElement("name", company.Name), from employee in company.Employees
			                                                                                                                                                           select new XElement("employee", new XElement("id", employee.Id), new XElement("uri", employee.Uri), new XElement("dateOfBirth", employee.DateOfBirth)))).RemoveEmptyNamespaces(ns);

			return xml.ToXmlString(encoding).RemoveXmlTagWhitespace();
		}

		[Test]
		public void SerializeXml_With_Ascii_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.ASCII;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Ascii_Encoding_With_And_Invalid_Namespace()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.ASCII;

			const string defaultNamespace = "test";

			GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding, defaultNamespace);

			// Assert
			var schemas = new XmlSchemaSet();
			schemas.Add(_executingAssembly.GetEmbeddedSchema("Company.xsd"));

			actual.ValidateSchema(schemas);
		}

		[Test]
		public void SerializeXml_With_Ascii_Encoding_With_And_Valid_Namespace()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.ASCII;

			const string defaultNamespace = "http://www.test.com/ns";

			GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding, defaultNamespace);

			// Assert
			var schemas = new XmlSchemaSet();
			schemas.Add(_executingAssembly.GetEmbeddedSchema("Company.xsd"));

			actual.ValidateSchema(schemas);
		}

		[Test]
		public void SerializeXml_With_BigEndianUnicode_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.BigEndianUnicode;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Default_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.Default;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Unicode_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.Unicode;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Utf32_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.UTF32;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Utf7_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.UTF7;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}

		[Test]
		public void SerializeXml_With_Utf8_Encoding()
		{
			// Arrange
			Company company = new MockCompany();

			var encoding = Encoding.UTF8;

			const string defaultNamespace = "http://www.test.com/ns";

			var expected = GetCompanyXml(company, encoding, defaultNamespace);

			// Act
			var actual = company.SerializeXml(encoding);

			// Assert
			Assert.AreEqual(expected, actual);
		}
	}
}